package com.example.demo.service.impl;

import com.example.demo.mapper.CarMapper;
import com.example.demo.mapper.CarModelMapper;
import com.example.demo.mapper.ReservationMapper;
import com.example.demo.service.ReservationService;
import com.example.demo.utils.ApiResponse;
import com.example.demo.utils.CarCount;
import com.example.demo.utils.JwtUtil;
import com.example.demo.utils.ReservationResult;
import com.example.demo.utils.Reservation;
import com.example.demo.utils.RenewalReservationRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

@Service
@EnableTransactionManagement
public class ReservationServiceImpl implements ReservationService {

    @Autowired
    private ReservationMapper reservationMapper;

    @Autowired
    private CarModelMapper carModelMapper;

    @Autowired
    private CarMapper carMapper;

    public static Date stringToSqlDate(String strDate) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        java.util.Date utilDate = format.parse(strDate);
        return new Date(utilDate.getTime());
    }

    public static LocalDate getCurrentDate() {
        return LocalDate.now();
    }

    public static LocalDate addDays(LocalDate date, int days) {
        return date.plusDays(days);
    }

    public static Optional<CarCount> findCarCountByModelID(List<CarCount> carCounts, String carModelID) {
        return carCounts.stream()
                .filter(carCount -> carCount.getCarModelID().equals(carModelID))
                .findFirst();
    }

    private static final Random RANDOM = new Random();

    public String generateReservationNumber(String userId) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); // Including milliseconds
        String dateTime = LocalDateTime.now().format(formatter);
        String randomSuffix = String.format("%04d", RANDOM.nextInt(10000)); // 4-digit random number
        return "RES-" + userId + "-" + dateTime + "-" + randomSuffix;
    }

    @Transactional
    @Override
    public ApiResponse getReservation(String storeId, String date) {
        try {
            Date from = stringToSqlDate(date);
            LocalDate now = getCurrentDate();

            if (from.toLocalDate().isBefore(now)) {
                return new ApiResponse(400, "查询日期不能早于今天", null);
            }

            List<CarCount> cars = carMapper.selectCountByStoreID(storeId);
            List<Reservation> reservations = reservationMapper.getAllPossibleReservation(storeId);
            LocalDate processingDate = now;

            while (!from.toLocalDate().isBefore(processingDate)) {
                for (Reservation reservation : reservations) {
                    updateCarCountBasedOnReservationStatus(reservation, storeId, processingDate, cars, now);
                }
                processingDate = addDays(processingDate, 1);
            }

            List<ReservationResult> reservationResults = new ArrayList<>();
            for (CarCount carCount : cars) {
                if (carCount.getCount() > 0) {
                    ReservationResult reservationResult = new ReservationResult();
                    reservationResult.setAmount(carCount.getCount());
                    reservationResult.setCarModel(carModelMapper.selectById(carCount.getCarModelID()));
                    reservationResults.add(reservationResult);
                }
            }

            return new ApiResponse(200, "查询成功", reservationResults);
        } catch (ParseException e) {
            return new ApiResponse(400, "日期解析错误", null);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public synchronized ApiResponse checkReservation(String token, Reservation reservation) {
        try {
            String userId = JwtUtil.getId(token);
            LocalDate from = reservation.getExpectedCheckoutTime().toLocalDate();
            LocalDate now = getCurrentDate();
            String checkoutStoreID = reservation.getExpectedCheckoutStoreID();
            CarCount carCount = carMapper.selectByStoreIDCarModelID(checkoutStoreID, reservation.getCarModelID());

            if (carCount == null) {
                return new ApiResponse(400, "预约失败，不存在相关车型", null);
            }

            List<Reservation> reservations = reservationMapper.getAllPossibleReservation(checkoutStoreID);
            LocalDate processingDate = now;

            while (!from.isBefore(processingDate)) {
                for (Reservation checkReservation : reservations) {
                    updateOneCarCountBasedOnReservationStatus(now, processingDate, checkoutStoreID, carCount, checkReservation);
                }
                processingDate = addDays(processingDate, 1);
            }

            if (carCount.getCount() > 0) {
                reservation.setUserID(userId);
                reservation.setReservationStatus("未借出");
                reservation.setRenewalPermission("是");
                reservation.setReservationID(generateReservationNumber(userId));
                reservationMapper.insert(reservation);
                return new ApiResponse(200, "成功预约", reservation);
            } else {
                return new ApiResponse(401, "相关车型数量不足", null);
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public synchronized ApiResponse renewReservation(String token, RenewalReservationRequest renewalReservationRequest) {
        try {
            String userId = JwtUtil.getId(token);
            Reservation reservation = reservationMapper.selectById(renewalReservationRequest.getReservationID());
            if (reservation == null) {
                return new ApiResponse(412, "相关订单不存在", null);
            }
            if (reservation.getRenewalPermission().equals("否")) {
                return new ApiResponse(413, "没有续订权限", null);
            }
            if (!reservation.getReservationStatus().equals("借出")) {
                return new ApiResponse(414, "订单状态异常", null);
            }
            LocalDate from = reservation.getExpectedReturnTime().toLocalDate();
            LocalDate now = getCurrentDate();
            String checkoutStoreID = reservation.getExpectedReturnStoreID();
            CarCount carCount = carMapper.selectByStoreIDCarModelID(checkoutStoreID, reservation.getCarModelID());

            if (carCount == null) {
                return new ApiResponse(400, "预约失败，不存在相关车型", null);
            }

            List<Reservation> reservations = reservationMapper.getAllPossibleReservation(checkoutStoreID);
            LocalDate processingDate = now;

            while (!from.isBefore(processingDate)) {
                for (Reservation checkReservation : reservations) {
                    updateOneCarCountBasedOnReservationStatus(now, processingDate, checkoutStoreID, carCount, checkReservation);
                }
                processingDate = addDays(processingDate, 1);
            }

            if (carCount.getCount() > 0) {
                Reservation newReservation = new Reservation(
                        generateReservationNumber(userId), userId,
                        reservation.getExpectedCheckoutStoreID(), renewalReservationRequest.getExpectedReturnStoreID(),
                        reservation.getCarModelID(), reservation.getExpectedCheckoutTime(),
                        renewalReservationRequest.getExpectedReturnTime(), reservation.getReservationStatus(), "否"
                );
                reservationMapper.insert(newReservation);
                reservationMapper.updateReservation(reservation.getReservationID(), "已还", "否");
                return new ApiResponse(200, "成功预约", newReservation);
            } else {
                return new ApiResponse(401, "相关车型数量不足", null);
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public synchronized ApiResponse cancelReservation(String token, String ReservationID) {
        try {
            String userId = JwtUtil.getId(token);
            Reservation reservation = reservationMapper.selectById(ReservationID);
            if (reservation == null) {
                return new ApiResponse(404, "没有找到该用户的订单信息", null);
            }
            if (!reservation.getUserID().equals(userId)) {
                return new ApiResponse(405, "用户信息错误", null);
            }
            if(!reservation.getReservationStatus().equals("未借出")){
                return  new ApiResponse(406, "订单状态错误", null);
            }
            reservationMapper.updateReservation(reservation.getReservationID(), "作废", "否");
            return new ApiResponse(200, "预定取消成功", reservation);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional
    @Override
    public ApiResponse getUserReservation(String token) {
        try {
            String userId = JwtUtil.getId(token);
            List<Reservation> reservations = reservationMapper.getReservationByUserID(userId);
            return new ApiResponse(200, "预定获取成功", reservations);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void updateOneCarCountBasedOnReservationStatus(LocalDate now, LocalDate time, String checkoutStoreID, CarCount carCount, Reservation checkReservation) {
        switch (checkReservation.getReservationStatus()) {
            case "未借出" -> {
                if (checkReservation.getExpectedCheckoutTime().toLocalDate().isEqual(time) && checkReservation.getExpectedCheckoutStoreID().equals(checkoutStoreID)) {
                    carCount.setCount(carCount.getCount() - 1);
                }
                if (checkReservation.getExpectedReturnTime().toLocalDate().isEqual(time) && checkReservation.getExpectedReturnStoreID().equals(checkoutStoreID)) {
                    carCount.setCount(carCount.getCount() + 1);
                }
            }
            case "已借出" -> {
                if (checkReservation.getExpectedReturnTime().toLocalDate().isEqual(time) && checkReservation.getExpectedReturnStoreID().equals(checkoutStoreID)) {
                    carCount.setCount(carCount.getCount() + 1);
                }
            }
            case "逾期" -> {
                if (checkReservation.getExpectedReturnTime().toLocalDate().isEqual(now) && checkReservation.getExpectedReturnStoreID().equals(checkoutStoreID)) {
                    carCount.setCount(carCount.getCount() - 1);
                }
            }
        }
    }

    private void updateCarCountBasedOnReservationStatus(Reservation reservation, String storeId, LocalDate time, List<CarCount> cars, LocalDate now) {
        findCarCountByModelID(cars, reservation.getCarModelID()).ifPresent(carCount -> updateOneCarCountBasedOnReservationStatus(now, time, storeId, carCount, reservation));
    }
}
